﻿using System;
using System.Collections.Generic;
using System.Linq;
using Styx.Common;
using Styx.CommonBot;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace TuanHA_Combat_Routine
{
    public partial class Classname
    {
        #region AuraCache

        private class AuraCacheClass
        {
            internal DateTime AuraCacheExpireTime { get; set; }
            internal ulong AuraCacheUnit { get; set; }
            internal WoWAura AuraCacheAura { get; set; }
            internal string AuraCacheName { get; set; }
            internal int AuraCacheId { get; set; }
        }

        private static readonly List<AuraCacheClass> AuraCacheList = new List<AuraCacheClass>();
        private static TimeSpan AuraCacheExpire;

        //////done
        private static void AuraCacheUpdate(WoWUnit target, bool ForceUpdate = false)
        {
            var AuraCacheUnit = AuraCacheList.FirstOrDefault(unit => unit.AuraCacheUnit == target.Guid);

            if (ForceUpdate)
            {
                if (AuraCacheUnit != null)
                {
                    AuraCacheList.RemoveAll(unit => AuraCacheUnit.AuraCacheUnit == target.Guid);
                }

                var expireTime = DateTime.Now + AuraCacheExpire;
                try
                {
                    foreach (WoWAura aura in target.GetAllAuras())
                    {
                        //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
                        AuraCacheList.Add(new AuraCacheClass
                            {
                                AuraCacheExpireTime = expireTime,
                                AuraCacheUnit = target.Guid,
                                AuraCacheAura = aura,
                                AuraCacheName = aura.Name,
                                AuraCacheId = aura.SpellId,
                            });
                    }
                }
                catch (Exception)
                {
                    Logging.Write("Error AuraCacheList.Add");
                    throw;
                }

            }

            if (AuraCacheUnit == null)
            {
                //Logging.Write("AuraCacheList Do Not Containt {0} Add All Aura to AuraCacheList", target.SafeName);
                var expireTime = DateTime.Now + AuraCacheExpire;
                try
                {
                    foreach (var aura in target.GetAllAuras())
                    {
                        //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
                        AuraCacheList.Add(new AuraCacheClass
                            {
                                AuraCacheExpireTime = expireTime,
                                AuraCacheUnit = target.Guid,
                                AuraCacheAura = aura,
                                AuraCacheName = aura.Name,
                                AuraCacheId = aura.SpellId,
                            });
                    }
                }
                catch (Exception)
                {
                    Logging.Write("Error AuraCacheList.Add");
                    throw;
                }
                //Logging.Write("Total AuraCacheList for {0}", target.SafeName);
                //foreach (var aura in AuraCacheList)
                //{
                //    if (aura.AuraCacheUnit != target)
                //    {
                //        continue;
                //    }
                //    Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
                //}
            }
            else if (AuraCacheUnit.AuraCacheExpireTime < DateTime.Now)
            {
                //Logging.Write("AuraCacheList Containt {0} But Cache Time Expired, Update All Aura to AuraCacheList",
                //              target.SafeName);

                AuraCacheList.RemoveAll(unit => AuraCacheUnit.AuraCacheUnit == target.Guid);

                var expireTime = DateTime.Now + AuraCacheExpire;
                foreach (WoWAura aura in target.GetAllAuras())
                {
                    //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
                    AuraCacheList.Add(new AuraCacheClass
                        {
                            AuraCacheExpireTime = expireTime,
                            AuraCacheUnit = target.Guid,
                            AuraCacheAura = aura,
                            AuraCacheName = aura.Name,
                            AuraCacheId = aura.SpellId,
                        });
                }

                //Logging.Write("Total AuraCacheList for {0}", target.SafeName);
                //foreach (var aura in AuraCacheList)
                //{
                //    if (aura.AuraCacheUnit != target)
                //    {
                //        continue;
                //    }
                //    Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
                //}
            }
            //else
            //{
            //    Logging.Write("AuraCacheList Containt {0} and Cache Time NOT Expired, Do Not update to AuraCacheList",
            //                  target.SafeName);

            //    Logging.Write("Total AuraCacheList for {0}", target.SafeName);
            //    foreach (var aura in AuraCacheList)
            //    {
            //        if (aura.AuraCacheUnit != target)
            //        {
            //            continue;
            //        }
            //        Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
            //    }
            //}
        }

        #endregion

        #region SpellsCooldownHS

        private static readonly HashSet<string> SpellsCooldownHS = new HashSet<string>
            {
                //Racial
                "Arcane Torrent",
                "Berserking",
                "Blood Fury",
                "Cannibalize",
                "Darkflight",
                "Escape Artist",
                "Every Man for Himself",
                "Gift of the Naaru",
                "Rocket Jump",
                "Shadowmeld",
                "Stoneform",
                "War Stomp",
                "Will of the Forsaken",

                //Class Spells
                //"Avenger's Shield",
                "Avenging Wrath",
                "Ardent Defender",
                "Blinding Light",
                "Beacon of Light",
                "Consecration",
                "Crusader Strike",
                "Devotion Aura",
                "Divine Favor ",
                "Divine Plea",
                "Divine Protection",
                "Divine Shield",
                "Execution Sentence",
                "Fist of Justice",
                "Guardian of Ancient Kings",
                "Hammer of Justice",
                "Hammer of the Righteous",
                //"Hammer of Wrath",
                "Hand of Freedom",
                "Hand of Protection",
                "Hand of Purity",
                "Hand of Sacrifice",
                "Hand of Salvation",
                "Holy Avenger",
                "Holy Prism",
                "Holy Shock", //It reset
                "Judgment",
                "Lay on Hands",
                "Light's Hammer",
                "Rebuke",
                "Reckoning",
                "Repentance",
                "Sacred Shield",
                "Speed of Light",
                "Turn Evil",
            };

        #endregion

        #region ClearEnemyandFriendListCache

        private static void ClearEnemyandFriendListCacheEvent(object sender, LuaEventArgs args)
        {
            //Logging.Write("=============================");
            //Logging.Write("ClearEnemyandFriendListCacheEvent");
            //Logging.Write("=============================");

            GlobalCheckTime = DateTime.Now - TimeSpan.FromSeconds(30);

            EnemyListCache.Clear();
            FriendListCache.Clear();
        }

        #endregion

        #region EnemyListCache

        private static readonly Dictionary<ulong, DateTime> EnemyListCache = new Dictionary<ulong, DateTime>();

        private static void EnemyListCacheClear()
        {
            //var indexToRemove = new List<ulong>();

            //foreach (var unit in EnemyList)
            //{
            //    if (unit.Value + TimeSpan.FromSeconds(ExpireSeconds) < DateTime.Now)
            //    {
            //        indexToRemove.Add(unit.Key);
            //    }
            //}

            var indexToRemove = EnemyListCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();

            //if (indexToRemove.Count > 0)
            //{
            //    Logging.Write("EnemyList To Be Removed");
            //    foreach (var unit in EnemyList)
            //    {
            //        Logging.Write("Key {0} Value {1}", unit.Key, unit.Value);
            //    }
            //    Logging.Write("_______________________");
            //}

            foreach (var index in indexToRemove)
            {
                //Logging.Write("Remove {0} from EnemyList", EnemyList[index]);
                EnemyListCache.Remove(index);
            }

            //if (indexToRemove.Count <= 0) return;
            //Logging.Write("EnemyList After Removed");
            //foreach (var unit in EnemyList)
            //{
            //    Logging.Write("Key {0} Value {1}", unit.Key, unit.Value);
            //}
            //Logging.Write("_______________________");
        }

        private static void EnemyListCacheAdd(WoWUnit unit, int expireSeconds = 10)
        {
            if (HasAuraArenaPreparation || HasAuraPreparation)
            {
                return;
            }
            if (EnemyListCache.ContainsKey(unit.Guid))
            {
                return;
            }
            //Logging.Write("Add {0} ({1}) to EnemyListCache", unit.Guid, unit.Name);
            EnemyListCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromSeconds(expireSeconds));
        }

        #endregion

        #region FriendListCache

        private static readonly Dictionary<ulong, DateTime> FriendListCache = new Dictionary<ulong, DateTime>();

        private static void FriendListCacheClear()
        {
            //var indexToRemove = new List<ulong>();

            //foreach (var unit in FriendList)
            //{
            //    if (unit.Value + TimeSpan.FromSeconds(ExpireSeconds) < DateTime.Now)
            //    {
            //        indexToRemove.Add(unit.Key);
            //    }
            //}

            var indexToRemove = FriendListCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();

            //if (indexToRemove.Count > 0)
            //{
            //    Logging.Write("FriendList To Be Removed");
            //    foreach (var unit in FriendList)
            //    {
            //        Logging.Write("Key {0} Value {1}", unit.Key, unit.Value);
            //    }
            //    Logging.Write("_______________________");
            //}

            foreach (var index in indexToRemove)
            {
                //Logging.Write("Remove {0} from FriendList", FriendList[index]);
                FriendListCache.Remove(index);
            }

            //if (indexToRemove.Count <= 0) return;
            //Logging.Write("FriendList After Removed");
            //foreach (var unit in FriendList)
            //{
            //    Logging.Write("Key {0} Value {1}", unit.Key, unit.Value);
            //}
            //Logging.Write("_______________________");
        }

        private static void FriendListCacheAdd(WoWUnit unit, int expireSeconds = 60)
        {
            if (FriendListCache.ContainsKey(unit.Guid)) return;
            //Logging.Write("Add {0} ({1}) to FriendListCache", unit.Guid, unit.Name);
            FriendListCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromSeconds(expireSeconds));
        }

        #endregion

        #region InLineOfSpellSightCache

        private static readonly Dictionary<ulong, DateTime> InLineOfSpellSightCache = new Dictionary<ulong, DateTime>();

        private static void InLineOfSpellSightCacheClear()
        {
            var indexToRemove = InLineOfSpellSightCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                InLineOfSpellSightCache.Remove(index);
            }
        }

        private static void InLineOfSpellSightCacheAdd(WoWUnit unit, int expireMilliseconds = 100)
        {
            if (InLineOfSpellSightCache.ContainsKey(unit.Guid)) return;
            //Logging.Write("Add {0} ({1}) to InLineOfSpellSightCache", unit.Guid, unit.Name);
            InLineOfSpellSightCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromMilliseconds(expireMilliseconds));
        }

        private static bool InLineOfSpellSightCheck(WoWUnit unit)
        {
            if (unit == null || !unit.IsValid)
            {
                return false;
            }

            InLineOfSpellSightCacheClear();

            if (unit == Me || InLineOfSpellSightCache.ContainsKey(unit.Guid))
            {
                return true;
            }

            if (unit.InLineOfSpellSight)
            {
                if (InRaid || InDungeon)
                {
                    InLineOfSpellSightCacheAdd(unit, 300);
                    return true;
                }
                InLineOfSpellSightCacheAdd(unit);
                return true;
            }
            return false;
        }

        #endregion

        #region CanCastCache

        private static readonly Dictionary<string, DateTime> SpellsCooldownCache = new Dictionary<string, DateTime>();
        //private static DateTime SkipOnGcdSpellCheck;
        private static DateTime spellCooldownBack;

        private static void SpellsCooldownCacheAdd(string spellName, DateTime spellCooldownBack)
        {
            //if (!SpellsCooldownHS.Contains(spellName))
            //{
            //    //Logging.Write("There's no {0} in SpellsCooldownCache, skip SpellsCooldownCacheAdd", spellName);
            //    return;
            //}
            if (UseSpecialization == 1 && spellName == "Lava Burst")///需要更改
            {
                return;
            }

            if (spellName.Contains("Shock"))
            {
                SpellsCooldownCache.Add("Earth Shock", spellCooldownBack);
                SpellsCooldownCache.Add("Flame Shock", spellCooldownBack);
                SpellsCooldownCache.Add("Frost Shock", spellCooldownBack);
            }
            else if (spellName == "Primal Strike" || spellName == "Storm Strike" || spellName == "Stormblast")
            {
                SpellsCooldownCache.Add("Primal Strike", spellCooldownBack);
                SpellsCooldownCache.Add("Storm Strike", spellCooldownBack);
                SpellsCooldownCache.Add("Stormblast", spellCooldownBack);
            }
            else
            {
                SpellsCooldownCache.Add(spellName, spellCooldownBack);
            }
        }

        private static void SpellsCooldownCacheClear()
        {
            if (HasAuraArenaPreparation)
            {
                SpellsCooldownCache.Clear();
                return;
            }

            var indexToRemove = SpellsCooldownCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                try
                {
                    SpellsCooldownCache.Remove(index);
                }
                catch (Exception)
                {
                    Logging.Write("Fail to SpellsCooldownCache.Remove({0})", index);
                    throw;
                }
            }
        }

        private static bool CanCastCheck(string spellName, bool IsOffGCDSpell = false)
        {
            SpellsCooldownCacheClear();

            if (SpellsCooldownCache.ContainsKey(spellName))
            {
                //Logging.Write("SpellsCooldownCache contains {0}, skip check", spellName);
                return false;
            }

            if (!SpellManager.HasSpell(spellName))
            {
                return false;
            }
			
            if ((InArena || InBattleground) &&///需要更改
                spellName != "Thunderstorm" &&
                spellName != "Shamanistic Rage" &&
                spellName != "Tremor Totem" &&
                DebuffCC(Me))
            {
                return false;
            }
            if (IsOffGCDSpell && GetSpellCooldown(spellName).TotalMilliseconds <= 0)
            {
                return true;
            }

            ///if (!IsOffGCDSpell && SkipOnGcdSpellCheck > DateTime.Now)
            ///{
                //Logging.Write("CanCastCheck: Global Cooldown back time {0} - Now is {1}, SkipOffGCDSpellCheck check",
                //              SkipOnGcdSpellCheck.ToString("ss:fff"), DateTime.Now.ToString("ss:fff"));
            ///    return false;
            ///}

            if (spellName == "Holy Shock" &&
                Me.CurrentMana >= SpellManager.Spells["Holy Shock"].PowerCost &&
                SpellManager.Spells["Holy Shock"].CooldownTimeLeft.TotalMilliseconds <= MyLatency)
            {
                return true;
            }

            if ((!Me.IsCasting || Me.IsCasting && Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency))
            {
                ///if ((UseSpecialization == 3) && (((spellName == "Healing Raing") || (spellName == "Cleanse")) || (spellName == "Riptide")))
                ///{
                ///    spellCooldownBack = (DateTime.Now + GetSpellCooldown(spellName)) - (TimeSpan.FromMilliseconds(MyLatency) + TimeSpan.FromMilliseconds(300.0));
                ///}
                ///else
                ///{
                    spellCooldownBack = (DateTime.Now + GetSpellCooldown(spellName)) - TimeSpan.FromMilliseconds(MyLatency);
                ///}
                //////spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) - TimeSpan.FromMilliseconds(MyLatency);

                if (spellCooldownBack <= DateTime.Now)
                {
                    return true;
                }
            }


            ///if (SpellManager.CanCast(spellName))
            ///{
            ///    return true;
            ///}

            //Do not cache if your level too low
            ///if (!CanCastCheckCacheUse)
            SpellsCooldownCacheAdd(spellName, spellCooldownBack);
            ///{
                //Logging.Write("CanCastCheckCacheUse {0}", CanCastCheckCacheUse);
            ///    return false;
            ///}

            ///SkipOnGcdSpellCheck = DateTime.Now + SpellManager.Spells[GCDCheckSpell].CooldownTimeLeft -
            ///                      TimeSpan.FromMilliseconds(MyLatency);

            //Logging.Write("SkipOffGCDSpellCheck Now {1} - {0}", SkipOnGcdSpellCheck.ToString("ss:fff"),
            //              DateTime.Now.ToString("ss:fff"));

            ///SpellsCooldownCacheAdd(spellName);

            return false;
        }

        #endregion

        #region CanUseCache

        private static readonly Dictionary<WoWItem, DateTime> ItemsCooldownCache = new Dictionary<WoWItem, DateTime>();
        private static DateTime ItemCooldownBack;

        private static TimeSpan GetItemCooldown(WoWItem item)
        {
            // Check for engineering tinkers!
            var itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell) || !item.Usable)
            {
                return TimeSpan.FromSeconds(30);
            }


            return item.CooldownTimeLeft;
        }

        private static void ItemsCooldownCacheAdd(WoWItem itemName, DateTime ItemCooldownBack)
        {
            //Logging.Write("{0} CooldownBack in {1} ms", ItemName, ItemCooldownBack);

            if (ItemCooldownBack > DateTime.Now)
            {
                //Logging.Write("Add {0} to ItemsCooldownCache, cooldown is back in {1} ms.", ItemName,
                //              ItemCooldownBack);
                ItemsCooldownCache.Add(itemName, ItemCooldownBack);
            }
        }

        private static void ItemsCooldownCacheClear()
        {
            var indexToRemove = ItemsCooldownCache.Where(
                unit =>
                unit.Value <= DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                ItemsCooldownCache.Remove(index);
            }
        }

        private static bool CanUseCheck(WoWItem ItemName)
        {
            ItemsCooldownCacheClear();


            if (ItemsCooldownCache.ContainsKey(ItemName))
            {
                //Logging.Write("ItemsCooldownCache contains {0}, skip check", ItemName);
                return false;
            }

            ItemCooldownBack = DateTime.Now + GetItemCooldown(ItemName);

            if (ItemCooldownBack <= DateTime.Now)
            {
                return true;
            }

            ItemsCooldownCacheAdd(ItemName, ItemCooldownBack);
            return false;
        }

        #endregion
    }
}